<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>apply</title>
</head>
<body>
    <script>
        /**
         * @author      haiyuan.wang
         * @date        ,2021.04.01
         * @vision      1.0
         * 
         * @description apply                                方法主要的作用是改变函数的执行环境
         * @example     fn.apply(thisArg, [argsArray])
         * @param       {Object}        thisArg              必选的。运行时的 this 值，非严格模式下，null 和 undefined 会自动指向 window
         * @param       {Array}         argsArray            可选的。执行过程中要传入的参数,是一个数组
        */

        // 第一个例子, 求一个数组中的最大值
        let goods = [1, 2, 3, 7, 6, 5, 4]
        const max = Math.max.apply(null, goods)
        console.log('max :>> ', max); // max :>>  7
        const min = Math.min.apply(null, goods)
        console.log('min :>> ', min); // min :>>  1

        // 写一个自己的 myApply 方法
        ;(function(window) {
            if(!Function.prototype.myApply) {
                Function.prototype.myApply = function(context, argsArray) {
                    let fn = this; // 谁调用 myApply 谁就是 this，在这里原函数就是this
                    if(context === null || typeof context === 'undefined') { // 判断是不是 null 或者 undefined
                        context = window
                    }
                    let key = Symbol('key');    // symbol 是唯一值
                    context[key] = fn;  // 将 fn 挂在context上面
                    let result = '';
                    if(!argsArray || argsArray?.length === 0) { // 不传第二个参数时，直接执行方法
                        result = context[key]()
                    } else {    //    传入第二个参数时，将参数展开，当作参数传入，执行函数
                        result = context[key](...argsArray)
                    }
                    delete context[key] // 执行完，删除掉该属性
                    return result
                }
            }
        })(window)
        let test = [1,5,9,12,15,19]
        const maxTest = Math.max.myApply(null, test)
        console.log('maxTest :>> ', maxTest); // maxTest :>>  19


        // 写一个自己的 _apply 方法
        ;(function(window) {
            if(!Function.prototype._apply) {
                Function.prototype._apply = function(context, argsArray) {
                    let fn = this; // 谁调用 _apply 谁就是 this，在这里原函数就是this
                    if(context === null || typeof context === 'undefined') { // 判断是不是 null 或者 undefined
                        context = window
                    }
                    let key = Symbol('key');    // symbol 是唯一值
                    context[key] = fn;  // 将 fn 挂在context上面
                    let result = '';
                    if(!argsArray || argsArray?.length === 0) { // 不传第二个参数时，直接执行方法
                        result = context[key]()
                    } else {    //    传入第二个参数时，将参数展开，当作参数传入，执行函数
                        let params = ''
                        argsArray.forEach(element => {
                            params += element + ','
                        });
                        params = params.slice(0, params.length-1)
                        console.log('params :>> ', params);  // params :>>  1,5,9,12,15,19
                        result = context[key](eval(params))
                    }
                    delete context[key] // 执行完，删除掉该属性
                    return result
                }
            }
        })(window)
        let test1 = [1,5,9,12,15,19]
        const maxTest1 = Math.max._apply(null, test1)
        console.log('maxTest1 :>> ', maxTest1); // maxTest1 :>>  19
    </script>
</body>
</html>